package com.lvmama.task.service.impl;

import com.lvmama.bigger.biz.service.ComTaskLogServiceInterface;
import com.lvmama.bigger.biz.vo.ComTaskLogVo;
import com.lvmama.bigger.common.data.domain.Sort;
import com.lvmama.bigger.common.entity.search.SearchOperator;
import com.lvmama.bigger.common.entity.search.Searchable;

import com.lvmama.comm.pet.po.pub.TaskResult;
import com.lvmama.task.common.service.TaskService;
import com.lvmama.task.common.service.TaskOperationLogService;
import com.lvmama.task.common.utils.Constant;
import com.lvmama.task.common.utils.DateUtil;
import com.lvmama.task.common.vo.Task;
import com.lvmama.task.common.vo.TaskJobLog;
import com.lvmama.task.service.dao.TaskDAO;
import net.sf.json.JSONObject;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;

import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Created by IntelliJ IDEA.<p/>
 * User: troy-kou<p/>
 * Date: 14-1-3<p/>
 * Time: 上午10:52<p/>
 * Email:kouhongyu@163.com<p/>
 */
public class TaskServiceImpl implements TaskService {

    private TaskDAO taskDAO;
    private TaskOperationLogService taskOperationLogService;
    private ComTaskLogServiceInterface biggerComTaskLogService;

    public Long getTaskCount(Map<String, Object> paramMap) {
        return taskDAO.getTaskCount(paramMap);
    }

    public List<Task> getTaskList(Map<String, Object> paramMap) {
        return taskDAO.getTaskList(paramMap);
    }

    @Override
    public List<Task> getTaskListByInTaskId(String selects) {
        return taskDAO.getTaskListByInTaskId(selects);
    }

    public void saveTask(Task task, String realName) {
        String logType;
        String typeName;
        String logInfo;

        if (task.getTaskId() == null) {
            taskDAO.insertTask(task);
            logType = "TASK_ADD";
            typeName = "新增任务";
            logInfo = "新增任务:" + JSONObject.fromObject(task).toString();
        } else {
            Task oldTask = taskDAO.getTask(task.getTaskId());
            taskDAO.updateTask(task);
            logType = "TASK_UPDATE";
            typeName = "更新任务";
            logInfo = "更新任务:" + getModify(oldTask, task);
        }
        taskOperationLogService.insert(task.getTaskId(), logType, typeName, logInfo, realName);
    }

    private String getModify(Task oldTask, Task task) {
        StringBuilder info = new StringBuilder();

        String infoTemplate = "【%s】从【%s】更新为【%s】;";

        if (!StringUtils.equals(oldTask.getTaskName(), task.getTaskName())) {
            info.append(String.format(infoTemplate, "任务名称", oldTask.getTaskName(), task.getTaskName()));
        }
        if (!StringUtils.equals(oldTask.getWebServiceUrl(), task.getWebServiceUrl())) {
            info.append(String.format(infoTemplate, "地址", oldTask.getWebServiceUrl(), task.getWebServiceUrl()));
        }
        if (!oldTask.getRequestType().equals(task.getRequestType())) {
            info.append(String.format(infoTemplate, "请求类型", oldTask.getRequestType(), task.getRequestType()));
        }
        if (!StringUtils.equals(oldTask.getStatus(), task.getStatus())) {
            info.append(String.format(infoTemplate, "运行状态", oldTask.getStatus(), task.getStatus()));
        }
        if (!(oldTask.getPlanTime() == null ? null == task.getPlanTime() : oldTask.getPlanTime().equals(task.getPlanTime()))) {
            info.append(String.format(infoTemplate, "计划时间",
                    DateUtil.formatDate(oldTask.getPlanTime(), DateUtil.PATTERN_yyyy_MM_dd_HH_mm_ss),
                    DateUtil.formatDate(task.getPlanTime(), DateUtil.PATTERN_yyyy_MM_dd_HH_mm_ss)));
        }
        if (!(oldTask.getNextRunTime() == null ? null == task.getNextRunTime() : oldTask.getNextRunTime().equals(task.getNextRunTime()))) {
            info.append(String.format(infoTemplate, "下次运行时间",
                    DateUtil.formatDate(oldTask.getNextRunTime(), DateUtil.PATTERN_yyyy_MM_dd_HH_mm_ss),
                    DateUtil.formatDate(task.getNextRunTime(), DateUtil.PATTERN_yyyy_MM_dd_HH_mm_ss)));
        }
        if (!StringUtils.equals(oldTask.getCycle(), task.getCycle())) {
            info.append(String.format(infoTemplate, "执行周期", oldTask.getCycle(), task.getCycle()));
        }
        if (!StringUtils.equals(oldTask.getWeek(), task.getWeek())) {
            info.append(String.format(infoTemplate, "每周几执行", oldTask.getWeek(), task.getWeek()));
        }
        if (!StringUtils.equals(oldTask.getStatus(), task.getStatus())) {
            info.append(String.format(infoTemplate, "任务状态", oldTask.getStatus(), task.getStatus()));
        }
        if (!StringUtils.equals(StringUtils.defaultString(oldTask.getDescription()), StringUtils.defaultString(task.getDescription()))) {
            info.append(String.format(infoTemplate, "任务描述", oldTask.getDescription(), task.getDescription()));
        }
        if (!StringUtils.equals(oldTask.getAvailable(), task.getAvailable())) {
            info.append(String.format(infoTemplate, "是否启用", oldTask.getAvailable(), task.getAvailable()));
        }
        if (!oldTask.getCycleDimension().equals(task.getCycleDimension())) {
            info.append(String.format(infoTemplate, "周期尺度", oldTask.getCycleDimension(), task.getCycleDimension()));
        }
        if (!StringUtils.equals(StringUtils.defaultString(oldTask.getParameter()), StringUtils.defaultString(task.getParameter()))) {
            info.append(String.format(infoTemplate, "参数", oldTask.getParameter(), task.getParameter()));
        }
        if (!StringUtils.equals(StringUtils.defaultString(oldTask.getCronExpression()), StringUtils.defaultString(task.getCronExpression()))) {
            info.append(String.format(infoTemplate, "Cron表达式", oldTask.getCronExpression(), task.getCronExpression()));
        }
        if (!StringUtils.equals(StringUtils.defaultString(oldTask.getOwner()), StringUtils.defaultString(task.getOwner()))) {
            info.append(String.format(infoTemplate, "环境标识", oldTask.getOwner(), task.getOwner()));
        }
        if (!StringUtils.equals(StringUtils.defaultString(oldTask.getJobNode()), StringUtils.defaultString(task.getJobNode()))) {
            info.append(String.format(infoTemplate, "调度节点", oldTask.getJobNode(), task.getJobNode()));
        }
        if ((oldTask.getSoTimeout() != null && !oldTask.getSoTimeout().equals(task.getSoTimeout()))
                || (task.getSoTimeout() != null && !task.getSoTimeout().equals(oldTask.getSoTimeout()))) {
            info.append(String.format(infoTemplate, "超时时间", oldTask.getSoTimeout(), task.getSoTimeout()));
        }

        return StringUtils.isBlank(info.toString()) ? "什么都没改还要点保存 :(" : info.toString();
    }

    public void saveTaskRuntime(Task task) {
        taskDAO.updateTaskRuntime(task);
    }

    public Task getTask(Long taskId) {
        return taskDAO.getTask(taskId);
    }

    public void delTask(String taskIds) {
        String[] ids = taskIds.split(",");
        for (String id : ids) {
            delTask(Long.valueOf(id));
        }
    }

    public void copyTask(String taskIds, Integer copyCount, String taskNamePrefix, String jobNode, String serial, String realName) {
        String[] ids = taskIds.split(",");
        for (String id : ids) {
            copyTask(Long.valueOf(id), copyCount, taskNamePrefix, jobNode, "Y".equals(serial), realName);
        }
    }

    public void enableDisableTask(String taskIds, String realName) {
        String[] ids = taskIds.split(",");
        for (String id : ids) {

            Task task = taskDAO.getTask(Long.valueOf(id));
            if (task != null) {
                String logType = "";
                String typeName = "";
                String logInfo;
                if (task.getAvailable().equals(Task.TASK_AVAILABLE.ENABLE.getCode())) {
                    task.setAvailable(Task.TASK_AVAILABLE.DISABLE.getCode());
                    logType = "TASK_DISABLE";
                    typeName = "任务停用";
                } else if (task.getAvailable().equals(Task.TASK_AVAILABLE.DISABLE.getCode())) {
                    task.setAvailable(Task.TASK_AVAILABLE.ENABLE.getCode());
                    logType = "TASK_DISABLE";
                    typeName = "任务启用";
                }
                taskDAO.updateTask(task);
                taskOperationLogService.insert(task.getTaskId(), logType, typeName, "修改任务启用状态为【" + task.getAvailable() + "】", realName);
            }
        }
    }

    public void immediatelyRunTask(String taskIds, String realName) {
        String[] ids = taskIds.split(",");

        for (String id : ids) {

            Task task = taskDAO.getTask(Long.valueOf(id));
            if (task != null
                    && !task.getAvailable().equals(Task.TASK_AVAILABLE.DISABLE.getCode())
                    && !task.getStatus().equals(Task.TASK_STATUS.RUN.getCode())) {

                task.setStatus(Task.TASK_STATUS.WAIT.getCode());
                task.setNextRunTime(new Date(System.currentTimeMillis()));

                taskDAO.updateTask(task);
                taskOperationLogService.insert(task.getTaskId(), "TASK_IMMEDIATELY_RUN", "立即运行", "修改任务状态为【立即运行】", realName);
            }
        }
    }

    public void batchModifyTask(String taskIds, Task taskParam, String realName) {
        List<Task> taskList = taskDAO.getTaskListByInTaskId(taskIds);

        Map<String, Integer> wildcardTaskName = null;
        if (StringUtils.isNotBlank(taskParam.getTaskName())) {
            wildcardTaskName = interceptedWildcard(taskParam.getTaskName());
        }
        Map<String, Integer> wildcardParameter = null;
        if (StringUtils.isNotBlank(taskParam.getParameter())) {
            wildcardParameter = interceptedWildcard(taskParam.getParameter());
        }

        for (Task task : taskList) {

            if (StringUtils.isNotBlank(taskParam.getTaskName())) {
                String taskName = taskParam.getTaskName();

                if (wildcardTaskName != null) {
                    for (String key : wildcardTaskName.keySet()) {
                        Integer index = wildcardTaskName.get(key);
                        taskName = taskName.replace(key, String.valueOf(Math.abs(index)));
                        wildcardTaskName.put(key, ++index);
                    }
                }

                task.setTaskName(taskName);
            }
            if (StringUtils.isNotBlank(taskParam.getParameter())) {
                String parameter = taskParam.getParameter();

                if (wildcardParameter != null) {
                    for (String key : wildcardParameter.keySet()) {
                        Integer index = wildcardParameter.get(key);
                        parameter = parameter.replace(key, String.valueOf(Math.abs(index)));
                        wildcardParameter.put(key, ++index);
                    }
                }

                task.setParameter(parameter);
            }
            if (StringUtils.isNotBlank(taskParam.getWebServiceUrl())) {
                task.setWebServiceUrl(taskParam.getWebServiceUrl());
            }
            if (taskParam.getRequestType() != null) {
                task.setRequestType(taskParam.getRequestType());
            }
            if (StringUtils.isNotBlank(taskParam.getStatus())) {
                task.setStatus(taskParam.getStatus());
            }
            if (StringUtils.isNotBlank(taskParam.getCycle())) {
                task.setCycle(taskParam.getCycle());
            }
            if (taskParam.getPlanTime() != null) {
                task.setPlanTime(taskParam.getPlanTime());
            }

            if (StringUtils.isNotBlank(taskParam.getDescription())) {
                task.setDescription(taskParam.getDescription());
            }
            if (StringUtils.isNotBlank(taskParam.getWeek())) {
                task.setWeek(taskParam.getWeek());
            }
            if (taskParam.getCycleDimension() != null) {
                task.setCycleDimension(taskParam.getCycleDimension());
            }
            if (StringUtils.isNotBlank(taskParam.getCronExpression())) {
                task.setCronExpression(taskParam.getCronExpression());
            }
            if (StringUtils.isNotBlank(taskParam.getOwner())) {
                task.setOwner(taskParam.getOwner());
            }
            if ((taskParam.getSoTimeout() != null && !taskParam.getSoTimeout().equals(task.getSoTimeout()))
                    || (task.getSoTimeout() != null && !task.getSoTimeout().equals(taskParam.getSoTimeout()))) {
                task.setSoTimeout(taskParam.getSoTimeout());
            }


            Task oldTask = taskDAO.getTask(task.getTaskId());

            taskDAO.updateTask(task);

            taskOperationLogService.insert(task.getTaskId(), "TASK_BATCH_MODIFY", "批量更新任务", "批量更新任务:" + getModify(oldTask, task), realName);
        }
    }

    public Integer cleanupTaskJobLog(Long taskId, Integer days) {

        Calendar calendar = Calendar.getInstance();
        calendar.add(Calendar.DAY_OF_MONTH, days * -1);

        return biggerComTaskLogService.cleanupComTaskLog(taskId, calendar.getTime());
    }

    public TaskJobLog addTaskJobLog(TaskJobLog taskJobLog) {
        ComTaskLogVo comTaskLogVo = new ComTaskLogVo();
        copyProperties(taskJobLog, comTaskLogVo);
        comTaskLogVo = biggerComTaskLogService.save(comTaskLogVo);
        copyProperties(comTaskLogVo, taskJobLog);

        return taskJobLog;
    }

    public void updateTaskJobLog(TaskJobLog taskJobLog) {
        ComTaskLogVo comTaskLogVo = new ComTaskLogVo();
        copyProperties(taskJobLog, comTaskLogVo);
        biggerComTaskLogService.update(comTaskLogVo);
    }

    public TaskJobLog getTaskJobLog(String taskJobLogId) {
        ComTaskLogVo comTaskLogVo = biggerComTaskLogService.findOne(taskJobLogId);
        TaskJobLog taskJobLog = new TaskJobLog();
        copyProperties(comTaskLogVo, taskJobLog);
        return taskJobLog;
    }

    public Long getTaskJobLogCount(Map<String, Object> params) {
        Searchable searchable = getSearchable(params);
        Long count = biggerComTaskLogService.count(searchable);
        return count != null ? count : 0;
    }

    public List<TaskJobLog> getTaskJobLogList(Map<String, Object> params) {

        Searchable searchable = getSearchable(params);

        Integer pageNumber = Integer.parseInt(params.get("page").toString());
        Integer pageSize = Integer.parseInt(params.get("pageSize").toString());
        searchable.setPage(pageNumber - 1, pageSize);

        searchable.addSort(getSort(params));

        List<ComTaskLogVo> list = biggerComTaskLogService.findAllWithSort(searchable);
        List<TaskJobLog> resList = new ArrayList<TaskJobLog>();
        if (list != null && !list.isEmpty()) {
            for (ComTaskLogVo vo : list) {
                TaskJobLog log = new TaskJobLog();
                copyProperties(vo, log);
                resList.add(log);
            }
        }
        return resList;
    }

    private Sort getSort(Map<String, Object> params) {
        Sort.Direction direction = Sort.Direction.DESC;
        String sortField = "startTime";

        if (params.get("sort") != null) {
            direction = Sort.Direction.fromString(String.valueOf(params.get("sort")));
        }
        if (params.get("sortField") != null) {
            sortField = String.valueOf(params.get("sortField"));
        }

        return  new Sort(direction, sortField);
    }

    private void copyProperties(ComTaskLogVo source, TaskJobLog target) {
        if (source != null && target != null) {
            if (source.getId() != null) {
                target.setTaskJobLogId(String.valueOf(source.getId()));
            }
            target.setLogId(source.getLogId());
            target.setTaskId(source.getTaskId());
            target.setTaskName(source.getTaskName());

            for (TaskResult.RUN_STATUS runStatus : TaskResult.RUN_STATUS.values()) {
                if (runStatus.getCode().equals(source.getResultRunStatus())) {
                    target.setResultRunStatus(runStatus);
                }
            }

            target.setResultInfo(source.getResultInfo());
            target.setExceptionInfo(source.getExceptionInfo());
            target.setStartTime(source.getStartTime());
            target.setEndTime(source.getEndTime());
            target.setTimeConsuming(source.getTimeConsuming());
        }
    }

    private void copyProperties(TaskJobLog source, ComTaskLogVo target) {
        if (source != null && target != null) {
            if (StringUtils.isNotBlank(source.getTaskJobLogId())) {
                target.setId(String.valueOf(source.getTaskJobLogId()));
            }
            target.setLogId(source.getLogId());
            target.setTaskId(source.getTaskId());
            target.setTaskName(source.getTaskName());
            target.setResultRunStatus(source.getResultRunStatus().getCode());
            target.setResultInfo(source.getResultInfo());
            target.setExceptionInfo(source.getExceptionInfo());
            target.setStartTime(source.getStartTime());
            target.setEndTime(source.getEndTime());
            target.setTimeConsuming(source.getTimeConsuming());
        }
    }

    private void delTask(Long taskId) {
        cleanupTaskJobLog(taskId, 0);
        taskDAO.delTask(taskId);
    }

    private Searchable getSearchable(Map<String, Object> params) {

        Map<String, Object> searchParams = new LinkedHashMap<String, Object>();

        Object id = params.get("taskJobLogId");
        if (id != null) {
            searchParams.put("id_" + SearchOperator.eq.name(), id);
        }

        Object resultRunStatus = params.get("resultRunStatus");
        if (resultRunStatus != null) {
            searchParams.put("resultRunStatus_" + SearchOperator.eq.name(), resultRunStatus);
        }

        Object taskId = params.get("taskId");
        if (taskId != null) {
            searchParams.put("taskId_" + SearchOperator.eq.name(), taskId);
        }

        Object startTime = params.get("startTime");
        if (startTime != null) {
            searchParams.put("endTime_" + SearchOperator.gte.name(), DateUtil.getDateByStr(String.valueOf(startTime), DateUtil.PATTERN_yyyy_MM_dd_HH_mm_ss));
        }
        Object endTime = params.get("endTime");
        if (endTime != null) {
            searchParams.put("startTime_" + SearchOperator.lte.name(), DateUtil.getDateByStr(String.valueOf(endTime), DateUtil.PATTERN_yyyy_MM_dd_HH_mm_ss));
        }

        Constant.TIME_CONSUMING_MILLIS maxTimeConsuming = (Constant.TIME_CONSUMING_MILLIS)params.get("maxTimeConsuming");
        if (maxTimeConsuming != null) {
            searchParams.put("timeConsuming_" + SearchOperator.lt.name(), maxTimeConsuming.getMillis());
        }

        Constant.TIME_CONSUMING_MILLIS minTimeConsuming = (Constant.TIME_CONSUMING_MILLIS)params.get("minTimeConsuming");
        if (minTimeConsuming != null) {
            searchParams.put("timeConsuming_" + SearchOperator.gt.name(), minTimeConsuming.getMillis());
        }

        return Searchable.newSearchable(searchParams);
    }

    private void copyTask(Long taskId, Integer copyCount, String taskNamePrefix, String jobNode, boolean isSerial, String realName) {
        Task task = taskDAO.getTask(taskId);

        for (int i = 0; i < copyCount; i++) {
            Task taskCopy = ObjectUtils.clone(task);

            String taskName = StringUtils.defaultString(taskNamePrefix) + (isSerial ? "【复制 " + (i + 1) + "】" : "") + task.getTaskName();

            taskCopy.setTaskId(null);
            taskCopy.setTaskName(taskName);
            taskCopy.setAvailable(Task.TASK_AVAILABLE.DISABLE.getCode());
            taskCopy.setStatus(Task.TASK_STATUS.END.getCode());
            taskCopy.setLastTime(null);
            taskCopy.setLastRunStatus(null);
            taskCopy.setNextRunTime(null);

            if (StringUtils.isNotBlank(jobNode)) {
                taskCopy.setJobNode(jobNode);
            }

            taskDAO.insertTask(taskCopy);
            taskOperationLogService.insert(taskCopy.getTaskId(), "TASK_COPY", "任务复制", "从【" + task.getTaskName() + "(" + task.getTaskId() + ")】复制任务：" + JSONObject.fromObject(taskCopy).toString(), realName);
        }
    }

    private Map<String, Integer> interceptedWildcard(String param) {

        Map<String, Integer> result = new HashMap<String, Integer>();
        Pattern pattern = Pattern.compile("\\$\\{-?([\\d])+\\}");
        Pattern pattern_num = Pattern.compile("(-?([\\d]))+");

        Matcher matcher = pattern.matcher(param);

        while (matcher.find()) {
            String key = matcher.group();
            Matcher matcher_num = pattern_num.matcher(key);
            matcher_num.find();

            result.put(key, Integer.parseInt(matcher_num.group()));
        }

        return result.isEmpty() ? null : result;
    }

    public void setTaskDAO(TaskDAO taskDAO) {
        this.taskDAO = taskDAO;
    }

    public void setTaskOperationLogService(TaskOperationLogService taskOperationLogService) {
        this.taskOperationLogService = taskOperationLogService;
    }

    public void setBiggerComTaskLogService(ComTaskLogServiceInterface biggerComTaskLogService) {
        this.biggerComTaskLogService = biggerComTaskLogService;
    }

}
